package com.codebo.homiematchbackend.controller;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.codebo.homiematchbackend.annotation.AuthCheck;
import com.codebo.homiematchbackend.common.BaseResponse;
import com.codebo.homiematchbackend.common.ErrorCode;
import com.codebo.homiematchbackend.common.ResultUtils;
import com.codebo.homiematchbackend.constant.CommonConstant;
import com.codebo.homiematchbackend.constant.UserConstant;
import com.codebo.homiematchbackend.exception.ThrowUtils;
import com.codebo.homiematchbackend.model.dto.blog.*;
import com.codebo.homiematchbackend.model.entity.User;
import com.codebo.homiematchbackend.model.entity.blog.Blog;
import com.codebo.homiematchbackend.model.entity.blog.BlogComment;
import com.codebo.homiematchbackend.model.vo.BlogCommentVO;
import com.codebo.homiematchbackend.model.vo.BlogVO;
import com.codebo.homiematchbackend.service.BlogCommentService;
import com.codebo.homiematchbackend.service.BlogService;
import com.codebo.homiematchbackend.service.UserService;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/blog")
public class BlogController
{

    @Resource
    private UserService userService;
    @Resource
    private BlogService blogService;

    @Resource
    private BlogCommentService blogCommentService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private TransactionTemplate transactionTemplate;

    private final Cache<String, String> LOCAL_CACHE = Caffeine.newBuilder()
            .initialCapacity(1024)
            .maximumSize(10_000L) // 最大 10000 条
            // 缓存 5 分钟后移除
            .expireAfterWrite(Duration.ofMinutes(5))
            .build();

    @GetMapping("/{id}")
    public BaseResponse<BlogVO> getBlogVOById(@PathVariable("id") Long id, HttpServletRequest httpServletRequest)
    {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        Blog blog = blogService.getById(id);
        ThrowUtils.throwIf(blog == null, ErrorCode.NOT_FOUND_ERROR);
        BlogVO blogVO = blogService.getBlogVO(blog, httpServletRequest);
        return ResultUtils.success(blogVO);
    }

    @PostMapping("/add")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Long> addBlog(BlogAddRequest blogAddRequest)
    {
        ThrowUtils.throwIf(blogAddRequest == null, ErrorCode.PARAMS_ERROR);
        long id = blogService.addBlog(blogAddRequest);
        return ResultUtils.success(id);
    }

    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteBlog(Long id)
    {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(blogService.getById(id) == null, ErrorCode.NOT_FOUND_ERROR);
        boolean result = blogService.removeById(id);
        return ResultUtils.success(result);
    }

    @GetMapping("/recent/{id}")
    public BaseResponse<List<BlogVO>> getRecentBlogVOList(@PathVariable("id") Long id, HttpServletRequest httpServletRequest)
    {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(userService.getById(id) == null, ErrorCode.NOT_FOUND_ERROR);
        List<Blog> blogList = blogService.list(new QueryWrapper<Blog>().eq("user_id", id).orderByDesc("create_time").last("limit 3"));
        List<BlogVO> blogVOList = blogService.getBlogVOList(blogList, httpServletRequest);
        return ResultUtils.success(blogVOList);
    }

    @PostMapping("/list/page/vo")
    public BaseResponse<Page<BlogVO>> listBlogVoByPage(BlogQueryRequest blogQueryRequest, HttpServletRequest httpServletRequest)
    {
        ThrowUtils.throwIf(blogQueryRequest == null, ErrorCode.PARAMS_ERROR);
        long current = blogQueryRequest.getCurrent();
        long size = blogQueryRequest.getPageSize();
        Page<Blog> blogPage = blogService.page(new Page<>(current, size), blogService.getQueryWrapper(blogQueryRequest));
        Page<BlogVO> blogVOPage = new Page<>(current, size, blogPage.getTotal());
        blogVOPage.setRecords(blogService.getBlogVOList(blogPage.getRecords(), httpServletRequest));
        return ResultUtils.success(blogVOPage);
    }

    @PostMapping("/list/recommend")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Page<BlogVO>> listBlogByRecommend(@RequestBody BlogQueryRequest blogQueryRequest, HttpServletRequest httpServletRequest)
    {
        ThrowUtils.throwIf(blogQueryRequest == null, ErrorCode.PARAMS_ERROR);
        Boolean isRefresh = blogQueryRequest.getIsRefresh();
        //使得缓存是同一份，不然构建的key不一样
        blogQueryRequest.setIsRefresh(false);
        ThrowUtils.throwIf(isRefresh == null, ErrorCode.PARAMS_ERROR);
        //构建缓存key
        String queryCondition = JSONUtil.toJsonStr(blogQueryRequest) + JSONUtil.toJsonStr(userService.getLoginUser(httpServletRequest));
        String hashKey = DigestUtils.md5DigestAsHex(queryCondition.getBytes());
        String cacheKey = String.format("HomieMatch:listBlogByRecommend:%s", hashKey);

        int current = blogQueryRequest.getCurrent();
        int pageSize = blogQueryRequest.getPageSize();
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();

        if (!isRefresh)
        {
            // 1. 先从本地缓存中查询
            String cachedValue = LOCAL_CACHE.getIfPresent(cacheKey);
            if (cachedValue != null)
            {
                // 如果缓存命中，返回结果
                List<BlogVO> cachedList = JSONUtil.toList(new JSONArray(cachedValue), BlogVO.class);
                Page<BlogVO> cachedPage = new Page<>(current, pageSize, cachedList.size());
                cachedPage.setRecords(cachedList);

                return ResultUtils.success(cachedPage);
            }
            // 2. 本地缓存未命中，查询 Redis 分布式缓存

            cachedValue = opsForValue.get(cacheKey);
            if (cachedValue != null)
            {
                // 如果缓存命中，更新本地缓存，返回结果
                LOCAL_CACHE.put(cacheKey, cachedValue);
                List<BlogVO> cachedList = JSONUtil.toList(new JSONArray(cachedValue), BlogVO.class);
                Page<BlogVO> cachedPage = new Page<>(current, pageSize, cachedList.size());
                cachedPage.setRecords(cachedList);
                return ResultUtils.success(cachedPage);
            }
        }


        // 3. 查询数据库
        //根据条件查询用户
//        Page<Blog> blogPage = blogService.page(new Page<>(current, pageSize), blogService.getQueryWrapper(blogQueryRequest));
        //按相似度进行排序
        List<BlogVO> voList = blogService.listBlogByRecommend(httpServletRequest, blogQueryRequest);
        if (voList.isEmpty())
        {
            return ResultUtils.success(new Page<>(current, pageSize));
        }

        // 4. 更新缓存
        // 更新 Redis 缓存
        String cacheValue = JSONUtil.toJsonStr(voList);
        // 设置缓存的过期时间，5 - 10 分钟过期，防止缓存雪崩
        int cacheExpireTime = 300 + RandomUtil.randomInt(0, 300);
        opsForValue.set(cacheKey, cacheValue, cacheExpireTime, TimeUnit.SECONDS);
        // 写入本地缓存
        LOCAL_CACHE.put(cacheKey, cacheValue);

        Page<BlogVO> blogVOPage = new Page<>(current, pageSize, voList.size());
        blogVOPage.setRecords(voList);
        return ResultUtils.success(blogVOPage);
    }

    @PostMapping("/like")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Boolean> likeBlog(@RequestBody LikeBlogRequest likeBlogRequest)
    {
        ThrowUtils.throwIf(likeBlogRequest == null, ErrorCode.PARAMS_ERROR);
        boolean result = blogService.likeBlog(likeBlogRequest);
        return ResultUtils.success(result);
    }

    @PostMapping("/collect")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Boolean> collectBlog(@RequestBody CollectBlogRequest collectBlogRequest)
    {
        ThrowUtils.throwIf(collectBlogRequest == null, ErrorCode.PARAMS_ERROR);
        boolean result = blogService.collectBlog(collectBlogRequest);
        return ResultUtils.success(result);
    }

    @PostMapping("/comment")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Boolean> commentBlog(@RequestBody CommentBlogRequest commentBlogRequest)
    {
        ThrowUtils.throwIf(commentBlogRequest == null, ErrorCode.PARAMS_ERROR);
        boolean result = blogService.commentBlog(commentBlogRequest);
        return ResultUtils.success(result);
    }

    @PostMapping("/view/{id}")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Boolean> viewBlog(@PathVariable Long id)
    {
        ThrowUtils.throwIf(id == null || id <= 0, ErrorCode.PARAMS_ERROR);
        UpdateWrapper<Blog> blogUpdateWrapper = new UpdateWrapper<>();
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        Boolean execute = transactionTemplate.execute(status ->
        {
            blogUpdateWrapper.eq("id", id)
                    .setSql("view_count = view_count + 1");
            boolean blogResult = blogService.update(blogUpdateWrapper);
            userUpdateWrapper.eq("id", blogService.getById(id).getUserId())
                    .setSql("blog_total_view =blog_total_view + 1");
            boolean userResult = userService.update(userUpdateWrapper);
            ThrowUtils.throwIf(!blogResult || !userResult, ErrorCode.OPERATION_ERROR);
            return true;

        });

        return ResultUtils.success(execute);
    }


    @PostMapping("/comment/page")
    public BaseResponse<Page<BlogCommentVO>> getBlogCommentPage(@RequestBody BlogCommentRequest blogCommentRequest)
    {
        Long blogId = blogCommentRequest.getBlogId();
        int current = blogCommentRequest.getCurrent();
        int pageSize = blogCommentRequest.getPageSize();
        String sortField = blogCommentRequest.getSortField();
        String sortOrder = blogCommentRequest.getSortOrder();
        QueryWrapper<BlogComment> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("blog_id", blogId)
                .orderBy(StrUtil.isNotBlank(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_DESC), sortField);
        Page<BlogComment> blogCommentPage = blogCommentService.page(new Page<>(current, pageSize), queryWrapper);
        Page<BlogCommentVO> blogCommentVOPage = new Page<>(current, pageSize, blogCommentPage.getTotal());
        blogCommentVOPage.setRecords(blogCommentService.getBlogCommentVOList(blogCommentPage.getRecords()));
        return ResultUtils.success(blogCommentVOPage);
    }


}
